package top.wshape1.shiziapi.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import top.wshape1.shiziapi.common.constant.BizCodeEnum;
import top.wshape1.shiziapi.common.constant.RedisConstant;
import top.wshape1.shiziapi.common.domain.CommonUser;
import top.wshape1.shiziapi.common.domain.ProjectSearch;
import top.wshape1.shiziapi.common.domain.to.ProjectUserIdTo;
import top.wshape1.shiziapi.common.domain.vo.PageVo;
import top.wshape1.shiziapi.common.exception.CustomException;
import top.wshape1.shiziapi.common.utils.R;
import top.wshape1.shiziapi.project.config.RabbitMqConfig;
import top.wshape1.shiziapi.project.domain.Member;
import top.wshape1.shiziapi.project.domain.Project;
import top.wshape1.shiziapi.project.domain.ProjectMembers;
import top.wshape1.shiziapi.project.domain.vo.ProjectVo;
import top.wshape1.shiziapi.project.feign.MessageFeignService;
import top.wshape1.shiziapi.project.feign.ThirdPartyFeignService;
import top.wshape1.shiziapi.project.feign.UaaFeignService;
import top.wshape1.shiziapi.project.mapper.ProjectMapper;
import top.wshape1.shiziapi.project.mapper.ProjectMembersMapper;
import top.wshape1.shiziapi.project.service.DocumentService;
import top.wshape1.shiziapi.project.service.LikeService;
import top.wshape1.shiziapi.project.service.ProjectService;
import top.wshape1.shiziapi.project.service.ReadService;
import top.wshape1.shiziapi.project.util.HotProjectUtil;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author Wshape1
 * @description 针对表【t_project】的数据库操作Service实现
 * @since 2023-09-12 15:40:51
 */
@Service
@Slf4j
public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project>
        implements ProjectService {

    @Resource
    private ProjectMembersMapper projectMembersMapper;


    @Resource
    private ThirdPartyFeignService thirdPartyFeignService;

    @Resource
    private UaaFeignService uaaFeignService;

    @Resource
    private MessageFeignService messageFeignService;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    @Lazy
    private DocumentService documentService;

    @Resource
    private LikeService likeService;

    @Resource
    private ReadService readService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    @Override
    public PageVo projectListPageMy(Long id, Long page, Long limit, String keyword) {
        Page<Project> qPage = this.page(PageDTO.of(page, limit),
                new LambdaQueryWrapper<Project>()
                        .eq(Project::getUserId, id)
                        .like(StringUtils.hasText(keyword), Project::getName, keyword)
                        .orderByDesc(Project::getCreateTime)
        );
        return new PageVo(qPage);
    }

    @Override
    public PageVo projectListPageHis(Long id, Long page, Long limit, String keyword) {

        List<Long> ids = projectMembersMapper.selectParticipatingProjectIds(id);
        Page<Project> qPage = PageDTO.of(page, limit);
        if (ids != null && !ids.isEmpty()) {
            qPage = this.page(qPage,
                    new LambdaQueryWrapper<Project>()
                            .in(Project::getId, ids)
                            .like(StringUtils.hasText(keyword), Project::getName, keyword)
                            .orderByDesc(Project::getCreateTime)
            );
        }

        return new PageVo(qPage);
    }

    @Transactional
    @Override
    public void createProject(Long userId, ProjectVo projectVo) {
        Project project = new Project();

        project.setUserId(userId);
        project.setName(projectVo.getName());
        project.setDescription(projectVo.getDescription());
        project.setIsPrivate(projectVo.getIsPrivate());
        Date date = new Date();
        project.setCreateTime(date);
        project.setUpdateTime(date);

        int insert = getBaseMapper().insert(project);

        ProjectMembers projectMembers = new ProjectMembers();
        projectMembers.setId(project.getId());
        projectMembers.setUserId(userId);
        projectMembers.setMembers("{}");
        int insert1 = projectMembersMapper.insert(projectMembers);

        if (insert > 0 && insert1 > 0)
            // 是公共的，存入elastic
            if (projectVo.getIsPrivate() == 0) {
                CompletableFuture.runAsync(() -> {
                    ProjectSearch projectSearch = new ProjectSearch();
                    projectSearch.setId(project.getId());
                    projectSearch.setName(project.getName());
                    projectSearch.setDescription(project.getDescription());
                    projectSearch.setCoverUrl(project.getCoverUrl());
                    projectSearch.setCreateTime(project.getCreateTime());
                    projectSearch.setUpdateTime(project.getUpdateTime());
                    projectSearch.setLikeCount(0L);
                    projectSearch.setReadCount(0L);
                    R<String> userNameById = uaaFeignService.getUserNameById(userId);
                    if (userNameById.isSuccessful()) {
                        projectSearch.setUserName(userNameById.getData());
                    }
//                    searchFeignService.save(projectSearch);
                    // 发送rabbit消息给search服务
                    projectSearch.setOperate(1);
                    rabbitTemplate.convertAndSend(
                            RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_EXCHANGE,
                            RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_BINDING,
                            projectSearch
                    );
                }, threadPoolExecutor);
            }

    }

    @Override
    public void modifyProject(Long userId, @Validated ProjectVo projectVo, MultipartFile file) {

        Project one = this.getById(projectVo.getId());

        if (one == null) {
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_NOT_EXIST_ERROR));
        }

        if (!one.getUserId().equals(userId)) {
            ProjectMembers projectMembers = projectMembersMapper.selectById(projectVo.getId());
            if (projectMembers.getMember(userId) == null) {
                throw new CustomException(R.instance(BizCodeEnum.PROJECT_PERMISSION_ERROR));
            }
        }

        LambdaUpdateWrapper<Project> updateWrapper = new LambdaUpdateWrapper<>();

        Date updateTime = new Date();

        updateWrapper
                .eq(Project::getId, projectVo.getId())
                .set(Project::getName, projectVo.getName())
                .set(Project::getDescription, projectVo.getDescription())
                .set(Project::getIsPrivate, projectVo.getIsPrivate())
                .set(Project::getUpdateTime, updateTime);

        String coverUrl = one.getCoverUrl();

        if (file != null && !file.isEmpty()) {
            // TODO 上传头像应该异步
            String[] split = Objects.requireNonNull(file.getOriginalFilename()).split("\\.");
            String suffix = split[split.length - 1];
            try {
                R<Object> r = thirdPartyFeignService.uploadCoverByBytes(projectVo.getId(), suffix, file.getBytes());
                updateWrapper.set(Project::getCoverUrl, r.getMsg());
                coverUrl = r.getMsg();
            } catch (Exception e) {
                log.error("modifyProject出现错误{}", e.getMessage());
            }
        }

        boolean update = this.update(updateWrapper);

        // 如果公开，则异步添加到elastic
        if (update && projectVo.getIsPrivate() == 0) {

            String finalCoverUrl = coverUrl;
            CompletableFuture.runAsync(() -> {

                // 如果是热点项目则修改
                if (!Objects.equals(one.getName(), projectVo.getName())) {
                    HotProjectUtil.updateCacheIfHot(redisTemplate, projectVo.getId(), RedisConstant.PROJECT_HOT_NAME_KEY, projectVo.getName());
                }

                // 修改es
                ProjectSearch projectSearch = new ProjectSearch();
                projectSearch.setId(projectVo.getId());
                projectSearch.setName(projectVo.getName());
                projectSearch.setDescription(projectVo.getDescription());
                projectSearch.setCoverUrl(finalCoverUrl);
                projectSearch.setCreateTime(one.getCreateTime());
                projectSearch.setUpdateTime(updateTime);

                projectSearch.setLikeCount(likeService.countLike(projectVo.getId()));
                projectSearch.setReadCount(readService.countRead(projectVo.getId()));

                R<String> userNameById = uaaFeignService.getUserNameById(one.getUserId());
                if (userNameById.isSuccessful()) {
                    projectSearch.setUserName(userNameById.getData());
                }
//                searchFeignService.save(projectSearch);
                // 发送rabbit消息给search服务
                projectSearch.setOperate(2);
                rabbitTemplate.convertAndSend(
                        RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_EXCHANGE,
                        RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_BINDING,
                        projectSearch
                );
                log.info("修改project信息发送给Search服务");
            }, threadPoolExecutor).exceptionally(
                    exception -> {
                        exception.printStackTrace();
                        return null;
                    }
            );

        } else if (update && one.getIsPrivate() == 0 && projectVo.getIsPrivate() == 1) {
            // 之前公开，现在变私密项目
            CompletableFuture.runAsync(() -> {
                //如果是热点项目则删除缓存
                HotProjectUtil.removeCacheIfHot(redisTemplate, projectVo.getId());

                // 删除es的数据
//                searchFeignService.remove(projectId);
                ProjectSearch projectSearch = new ProjectSearch();
                projectSearch.setId(projectVo.getId());
                projectSearch.setOperate(3);
                rabbitTemplate.convertAndSend(
                        RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_EXCHANGE,
                        RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_BINDING,
                        projectSearch
                );
            }, threadPoolExecutor);
        }

    }

    @Override
    public PageVo getProjectMemberPage(CommonUser user, String projectId, Long page, Long limit) {

        ProjectMembers projectMembers = projectMembersMapper.selectById(projectId);

        // 没有在member表中找到 数据
        if (projectMembers == null) {
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_NOT_EXIST_ERROR));
        }

        Set<Long> memberIds = projectMembers.getMemberMap().keySet();
        // 放入拥有者
        projectMembers.getMemberMap().put(projectMembers.getUserId(), new Member(projectMembers.getUserId(), 0));

        // 非项目成员查询成员列表 返回无权限
        if (projectMembers.getMemberMap().get(user.getId()) == null) {
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_PERMISSION_ERROR));
        }

        // 此段代码是去uaa模块查询用户的信息
        ArrayList<Long> list = new ArrayList<>(memberIds.size() + 1);
        // 把项目拥有者也导进去查找 并放在第一
        list.add(projectMembers.getUserId());
        list.addAll(memberIds);
        R<PageVo> r = uaaFeignService.getMemberPage(page, limit, list);

        List<Map<String, Object>> projectMemberVos = r.getData().getList().stream().map(pmvo -> {
            Map<String, Object> map = (Map<String, Object>) pmvo;
            // 添加type属性
            map.put("type", projectMembers.getMember(Long.parseLong(Integer.toString((int) map.get("id")))).getType());
            return map;
        }).toList();

        r.getData().setList(projectMemberVos);
        if (r.isSuccessful())
            return r.getData();
        else
            return new PageVo(PageDTO.of(page, limit));
    }

    @Override
    @Transactional
    public void deleteProject(Long userId, Long projectId) {

        Project project = this.getById(projectId);

        if (project == null) {
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_NOT_EXIST_ERROR));
        }

        if (!Objects.equals(project.getUserId(), userId)) {
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_PERMISSION_ERROR));
        }

        // 删除文档（文件夹，APi,MD,RTF）
        documentService.clearProject(projectId);

        // TODO 删除项目的Mock配置

        // 删除成员列表
        projectMembersMapper.deleteById(projectId);
        boolean b = this.removeById(projectId);

        if (b) {
            CompletableFuture.runAsync(() -> {
                // 如果是热点项目则删除缓存
                HotProjectUtil.removeCacheIfHot(redisTemplate, projectId);

                // 异步删除elastic中的数据
//                searchFeignService.remove(projectId);
                ProjectSearch projectSearch = new ProjectSearch();
                projectSearch.setId(projectId);
                projectSearch.setOperate(3);
                rabbitTemplate.convertAndSend(
                        RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_EXCHANGE,
                        RabbitMqConfig.SHIZIAPI_PROJECT_ELASTIC_BINDING,
                        projectSearch
                );
            }, threadPoolExecutor);
        }

    }

    @Override
    public void inviteMember(Long userId, String userName, Long projectId, Long memberId) {

        Project project = getById(projectId);

        if (project == null)
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_NOT_EXIST_ERROR));

        if (!Objects.equals(project.getUserId(), userId))
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_PERMISSION_ERROR));

        // 异步 发送邀请消息
        CompletableFuture.runAsync(() ->
                messageFeignService.sendInviteMsg(projectId, project.getName(), userId, userName, memberId), threadPoolExecutor);


    }

    @Override
    public void kickMember(Long userId, Long projectId, Long memberId) {

        Project project = getById(projectId);

        if (project == null)
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_NOT_EXIST_ERROR));

        if (!Objects.equals(project.getUserId(), userId))
            throw new CustomException(R.instance(BizCodeEnum.PROJECT_PERMISSION_ERROR));

        projectMembersMapper.deleteMemberFromProject(projectId, memberId);

    }

    @Override
    public void acceptInvite(Long msgId, Long memberId, String userBase64) {

        // 利用msgId查出projectId,添加成员
        R<ProjectUserIdTo> r = messageFeignService.getProjectUserId(msgId);

        if (r.isSuccessful()) {

            projectMembersMapper.updateMemberToProject(r.getData().getProjectId(), new Member(memberId, 2));
            // 调用远程，已读邀请消息
            CompletableFuture.runAsync(() -> {
                        try {
                            messageFeignService.readInviteMsg(msgId, userBase64);
                        } catch (Exception e) {
                            log.error(e.getMessage());
                        }
                    }
                    , threadPoolExecutor);
        }


    }

    @Override
    public void rejectInvite(Long msgId, CommonUser commonUser, String userBase64) {
        // 利用msgId查出projectId,添加成员
        R<ProjectUserIdTo> r = messageFeignService.getProjectUserId(msgId);

        if (r.isSuccessful()) {

            // 调用远程，已读邀请消息
            CompletableFuture.runAsync(() -> {
                        try {
                            messageFeignService.readInviteMsg(msgId, userBase64);
                            // 给拥有者发送用户拒绝邀请消息

                            Project project = this.getById(r.getData().getProjectId());
                            messageFeignService.sendRejectMsg(project.getId(), project.getName(), commonUser.getId(), commonUser.getNickname(), r.getData().getUserId());
                        } catch (Exception e) {
                            log.error(e.getMessage());
                        }
                    }
                    , threadPoolExecutor);
        }


    }

    @Override
    public String getNameById(Long projectId) {

        // 先查询是否是热点项目
        Object o = redisTemplate.opsForHash().get(RedisConstant.PROJECT_HOT_KEY + projectId, RedisConstant.PROJECT_HOT_NAME_KEY);
        if (o != null) {
            return (String) o;
        }

        Project one = this.getOne(new LambdaQueryWrapper<Project>()
                .eq(Project::getId, projectId)
                .select(Project::getName));
        return one.getName();
    }

    @Override
    public boolean hasPermissionToEdit(Long projectId, Long userIds) {

        ProjectMembers projectMembers = projectMembersMapper.selectById(projectId);

        return Objects.equals(projectMembers.getUserId(), userIds) ||
                Objects.nonNull(projectMembers.getMember(userIds));
    }

    @Override
    public List<Long> getPublicProjectIds() {
        return this.getBaseMapper().selectPublicProjectIds();
    }
}




